﻿using System;
using System.Collections.Generic;
using System.Text;
using Ivi.Driver.Interop;
using System.Text.RegularExpressions;
using AIRO.Devices.StepMotor;
using Ivi.ConfigServer.Interop;
using System.Reflection;
using System.Runtime.InteropServices;


namespace AIRO.Devices.StepMotor
{
    public enum SMC3ErrorCode : int
    {
        /// <summary>
        /// Нормальное выполнение команды
        /// </summary>
        NormalPerformanceOfACommand = 0,
        /// <summary>
        /// Остановка по датчику паузы или команде PS
        /// </summary>
        Paused = 2,
        /// <summary>
        /// Ошибка кода исполнительной команды
        /// </summary>
        InvalidCodeOfAnExecutiveCommand = 3,
        /// <summary>
        /// Завершение исполнительной программы, успешное исполнение команды PS
        /// 
        /// </summary>
        OperationComplete = 4,
        /// <summary>
        /// Ошибка приема по com-порту
        /// </summary>
        ComPortError = 5,
        /// <summary>
        /// Ошибка команды
        /// </summary>
        InvalidCommand = 6,
        /// <summary>
        /// Возобновление исполнения по команде PS или освобождению датчика паузы
        /// </summary>
        Resumed = 7,
        /// <summary>
        /// Ошибка данных команды
        /// </summary>
        InvalidCommandArguments = 8

    }

    [Guid("F1D6C402-28D5-4bc0-9D12-1C209582D833")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("MyDevices.StepMotor.StepMotorSMC3")]
    public class StepMotorSMC3 : BasicIviDriverVisa, IStepMotor, IIviDriver, IIviDriverIdentity, IIviComponentIdentity, IIviDriverOperation, IIviDriverUtility
    {
        StepMotorState _state;
        //System.IO.Ports.SerialPort serialPort;        
        int channel;
        int speed;
        int reverseSteps;
        bool reverseDirection;

        public StepMotorSMC3()
        {
            _description = "IVI Custom Driver for SMC-3 StepMotor Controller";
            _identifier = "SMC3";
            _instrumentManufacturer = "Electroprivod";
            _supportedInstrumentModels = "SMC-3";
            _vendor = "LNEP ISSP(RAS)";
        }

        #region IStepMotor Members

        public StepMotorState State
        {
            get
            {
                return _state;
            }
        }

        public void BeginMove(int steps)
        {
            throw new NotImplementedException();
        }

        public void Move(int steps)
        {
            if (_initialized)
            {
                _state = StepMotorState.Programing;
                OnStateChangedEvent(new StateChangedEventArgs(StepMotorState.Programing));
                if (Load(channel) == SMC3ErrorCode.NormalPerformanceOfACommand)
                    if (Begin() == SMC3ErrorCode.NormalPerformanceOfACommand)
                        if (Enable() == SMC3ErrorCode.NormalPerformanceOfACommand)
                        {
                            SMC3ErrorCode res;
                            if (steps >= 0)
                            {
                                res = DirRight();
                            }
                            else
                            {
                                res = DirLeft();
                            }
                            bool doReverse = !((steps >= 0) ^ (reverseDirection));
                            if (res == SMC3ErrorCode.NormalPerformanceOfACommand)
                            {
                                if (SetSpeed(speed) == SMC3ErrorCode.NormalPerformanceOfACommand)
                                {
                                    int stepsCnt;
                                    if (doReverse)
                                    {
                                        stepsCnt = Math.Abs(steps) + reverseSteps;
                                    }
                                    else
                                    {
                                        stepsCnt = Math.Abs(steps);
                                    }

                                    if (SM3Move(stepsCnt) == SMC3ErrorCode.NormalPerformanceOfACommand)
                                    {
                                        res = SMC3ErrorCode.NormalPerformanceOfACommand;
                                        if (doReverse)
                                            if (Reverse() == SMC3ErrorCode.NormalPerformanceOfACommand)
                                                res = SM3Move(reverseSteps);
                                        if (res == SMC3ErrorCode.NormalPerformanceOfACommand)
                                            if (End() == SMC3ErrorCode.NormalPerformanceOfACommand)
                                            {
                                                _state = StepMotorState.Running;
                                                OnStateChangedEvent(new StateChangedEventArgs(StepMotorState.Running));
                                                if (Start() == SMC3ErrorCode.OperationComplete)
                                                {
                                                    _state = StepMotorState.Idle;
                                                    OnStateChangedEvent(new StateChangedEventArgs(StepMotorState.Idle));
                                                    OnFinishEvent(new FinishEventArgs());
                                                    return;

                                                }
                                            }

                                    }

                                }
                            }
                        }
                _state = StepMotorState.Error;
                OnStateChangedEvent(new StateChangedEventArgs(StepMotorState.Error));
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        protected virtual void OnStateChangedEvent(StateChangedEventArgs e)
        {
            EventHandler<StateChangedEventArgs> handler = StateChangedEvent;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnFinishEvent(FinishEventArgs e)
        {
            EventHandler<FinishEventArgs> handler = FinishEvent;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        

        public event EventHandler<StateChangedEventArgs> StateChangedEvent;
        public event EventHandler<FinishEventArgs> FinishEvent;
        public event EventHandler<ProgressEventArgs> ProgressEvent;

        #endregion

        public void Disable()
        {
        }       

        #region Control Commands


        public SMC3ErrorCode Load(int channel)
        {

            string cmd = String.Format("LD{0}*", channel);
            return Query(cmd);
        }

        public SMC3ErrorCode Read(int channel)
        {
            string cmd = String.Format("RD{0}*", channel);
            return Query(cmd);
        }

        public SMC3ErrorCode Start(int channel)
        {
            string cmd = String.Format("ST{0}*", channel);
            return Query(cmd);
        }

        public SMC3ErrorCode Start()
        {
            string cmd = "ST*";
            return Query(cmd);
        }

        public SMC3ErrorCode Pause(int channel)
        {
            string cmd = String.Format("PS{0}*", channel);
            return Query(cmd);
        }
        #endregion

        #region Executive Commands
        public SMC3ErrorCode Begin()
        {
            string cmd = "BG*";
            return Query(cmd);
        }

        public SMC3ErrorCode End()
        {
            string cmd = "ED*";
            return Query(cmd);
        }

        public SMC3ErrorCode DirLeft()
        {
            string cmd = "DL*";
            return Query(cmd);
        }

        public SMC3ErrorCode DirRight()
        {
            string cmd = "DR*";
            return Query(cmd);
        }

        public SMC3ErrorCode Reverse()
        {
            string cmd = "RS*";
            return Query(cmd);
        }

        public SMC3ErrorCode MicroOn()
        {
            string cmd = "ON*";
            return Query(cmd);
        }

        public SMC3ErrorCode MicroOff()
        {
            string cmd = "OF*";
            return Query(cmd);
        }

        public SMC3ErrorCode Acceleration(int acceleration)
        {
            string cmd;
            if (acceleration >= 0)
            {
                cmd = String.Format("AL+{0}*", acceleration);
            }
            else
            {
                cmd = String.Format("AL{0}*", acceleration);
            }
            return Query(cmd);
        }

        public SMC3ErrorCode SetSpeed(int speed)
        {
            string cmd = String.Format("SD{0}*", speed);
            return Query(cmd);
        }

        public SMC3ErrorCode SetStartSpeed(int speed)
        {
            string cmd = String.Format("SS{0}*", speed);
            return Query(cmd);
        }

        public SMC3ErrorCode SM3Move()
        {
            string cmd = "MV*";
            return Query(cmd);
        }

        public SMC3ErrorCode SM3Move(int steps)
        {
            string cmd = String.Format("MV{0}*", steps);
            return Query(cmd);
        }

        public SMC3ErrorCode Stop()
        {
            string cmd = "SP*";
            return Query(cmd);
        }

        public SMC3ErrorCode Stop(int time)
        {
            string cmd = String.Format("SP{0}*", time);
            return Query(cmd);
        }

        public SMC3ErrorCode Label()
        {
            string cmd = "LL*";
            return Query(cmd);
        }

        public SMC3ErrorCode Jump(int count)
        {
            string cmd = String.Format("JP{0}*", count);
            return Query(cmd);
        }

        public SMC3ErrorCode Enable()
        {
            string cmd = "EN*";
            return Query(cmd);
        }

        public SMC3ErrorCode SM3Disable()
        {
            string cmd = "DS*";
            return Query(cmd);
        }

        public SMC3ErrorCode SetFlag()
        {
            string cmd = "SF*";
            return Query(cmd);
        }
        public SMC3ErrorCode ClearFlag()
        {
            string cmd = "CF*";
            return Query(cmd);
        }
        public SMC3ErrorCode WaitLow()
        {
            string cmd = "WL*";
            return Query(cmd);
        }
        public SMC3ErrorCode WaitHigh()
        {
            string cmd = "WH*";
            return Query(cmd);
        }
        #endregion

        #region IIviDriver Members      

        protected override void ConfigureInitialSettings(IIviStructure confInitialSettings)
        {
            
            foreach (IIviDataComponent dataComp in confInitialSettings.DataComponents)
            {
                //System.Diagnostics.Debug.WriteLine(dataComp.Name);
                string name = dataComp.Name;
                switch (name.ToLower())
                {
                    case "channel":
                        channel = ((IIviInteger)dataComp).Value;
                        break;                    
                    case "speed":
                            speed = ((IIviInteger)dataComp).Value;                                                
                        break;
                    
                    case "reversedirection":                        
                            reverseDirection = ((IIviBoolean)dataComp).Value;                                                      
                        
                        break;
                    case "reversesteps":                        
                            reverseSteps = ((IIviInteger)dataComp).Value;                         
                            
                        
                        break;
                    
                    default:
                        iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_BAD_OPTION_NAME));
                        break;
                }


            }

        }
        
        public void Initialize(string ResourceName, bool IdQuery, bool Reset, string OptionString)
        {
            if (!_initialized)
            {
                _rangeCheck = true;
                _cache = true;
                _queryInstrumentStatus = false;
                _simulate = false;
                _driverSetup = "";
                _recordCoercions = false;
                _interchangeCheck = false;

                InitVisaMessageBasedSession(ResourceName);
                instrument.IO.Timeout = 1000;
                instrument.IO.TerminationCharacter = (byte)'*';
                instrument.IO.TerminationCharacterEnabled = true;
                ParseOptionString(OptionString);
                if (Reset)
                {
                    this.Reset();
                }
                _state = StepMotorState.Idle;               
                
                _initialized = true;               

            }
            else
            {
                //IviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_ALREADY_INITIALIZED));
                throw (new Exception("IVI_ALREADY_INITIALIZED"));
            }

        }

        #endregion

        private new SMC3ErrorCode Query(string cmd)
        {
            //mbSession.Write(cmd);
            //SMC3ErrorCode res = ParseAnswer(mbSession.ReadString());
            SMC3ErrorCode res = ParseAnswer(base.Query(cmd), cmd);
            //serialPort.Open();
            //serialPort.Write(cmd);
            //S3ErrorCode res =ParseAnswer(serialPort.ReadTo("*"));
            //serialPort.Close();
            return res;
        }

        private SMC3ErrorCode ParseAnswer(string answer, string cmd)
        {
            Regex answerRegex = new Regex(cmd + @"\s*E(?<channel>\d)(?<errorCode>\d)\*?", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            Match m = answerRegex.Match(answer);
            if (m.Success)
            {
                return (SMC3ErrorCode)System.Convert.ToInt32(m.Groups["errorCode"].Value);
            }
            else
            {
                throw (new Exception("Can't parse answer from the controller"));
            }
        }

    }
}
